package com.github.itdachen.gateway.oplog.impl;

import com.alibaba.fastjson2.JSONObject;
import com.github.itdachen.auth.interfaces.entity.OplogInfoClient;
import com.github.itdachen.auth.interfaces.oplog.IGatewayClientOplogRpc;
import com.github.itdachen.framework.context.id.IdUtils;
import com.github.itdachen.framework.context.permission.PermissionInfo;
import com.github.itdachen.framework.context.userdetails.UserInfoDetails;
import com.github.itdachen.framework.core.response.ServerResponse;
import com.github.itdachen.gateway.oplog.IGatewayOplogHandler;
import com.github.itdachen.gateway.utils.GateWayIpUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ServerWebExchange;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.*;

/**
 * RPC 处理日志
 *
 * @author 王大宸
 * @date 2025-02-10 10:26
 */
@Service
public class GatewayRpcOplogHandler implements IGatewayOplogHandler {
    private static final Logger logger = LoggerFactory.getLogger(GatewayRpcOplogHandler.class);


    private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 16, 3,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000),
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("gateway-oplog-thread-" + ThreadLocalRandom.current().nextInt(1000));
                    return thread;
                }
            });

    @DubboReference // 远程调用
    private IGatewayClientOplogRpc oplogRpc;


    /***
     * 添加日志基础信息
     *
     * @author 王大宸
     * @date 2025/2/10 10:16
     * @param webExchange webExchange
     * @param userInfoDetails userInfoDetails
     * @param oplogId oplogId
     * @return void
     */
    @Override
    public void saveOplog(ServerWebExchange webExchange,
                          UserInfoDetails userInfoDetails,
                          String oplogId) {
        ServerHttpRequest request = webExchange.getRequest();

        OplogInfoClient oplogInfo = getBizOplogInfo(userInfoDetails, oplogId);

        /* 基础访问信息 */
        oplogInfo.setRequestId(request.getId());
        oplogInfo.setRequestUri(request.getPath().value());
        oplogInfo.setRequestMethod(request.getMethod().name());
        oplogInfo.setRequestBody("");
        oplogInfo.setRequestTime(LocalDateTime.now());

        List<String> list = request.getHeaders().get("User-Agent");
        String userAgent = "";
        if (!list.isEmpty()) {
            userAgent = list.get(0);
        }
        oplogInfo.setUserAgent(userAgent);

        /* 访问主机信息 */
        oplogInfo.setHostIp(GateWayIpUtils.getIP(request));
        oplogInfo.setHostAddress("");
        oplogInfo.setHostOs(getOsInfo(userAgent));
        oplogInfo.setHostBrowser(getBrowserInfo(userAgent));

        /* 操作地址 */
        oplogInfo.setAddrCountryCode("86");
        oplogInfo.setAddrCountryCode("中国");
        oplogInfo.setAddrPoveCode("");
        oplogInfo.setAddrPoveName("");
        oplogInfo.setAddrCityCode("");
        oplogInfo.setAddrCityName("");
        oplogRpc.saveOplog(oplogInfo);
    }

    @Override
    public String saveOplog(ServerWebExchange webExchange, UserInfoDetails userInfoDetails, PermissionInfo permissionInfo) {
        if (null == permissionInfo) {
            return null;
        }

        ServerHttpRequest request = webExchange.getRequest();
        String oplogId = IdUtils.getId();
        OplogInfoClient oplogInfo = getBizOplogInfo(userInfoDetails, oplogId);

        /* 基础访问信息 */
        oplogInfo.setRequestId(request.getId());
        oplogInfo.setRequestUri(request.getPath().value());
        oplogInfo.setRequestMethod(request.getMethod().name());
        oplogInfo.setRequestBody("{}");
        oplogInfo.setRequestTime(LocalDateTime.now());

        oplogInfo.setElementType(permissionInfo.getType());
        oplogInfo.setElementTitle(permissionInfo.getName());

        List<String> list = request.getHeaders().get("User-Agent");
        String userAgent = "";
        if (!list.isEmpty()) {
            userAgent = list.get(0);
        }
        oplogInfo.setUserAgent(userAgent);

        /* 访问主机信息 */
        oplogInfo.setHostIp(GateWayIpUtils.getIP(request));
        oplogInfo.setHostAddress("");
        oplogInfo.setHostOs(getOsInfo(userAgent));
        oplogInfo.setHostBrowser(getBrowserInfo(userAgent));

        /* 操作地址 */
        oplogInfo.setAddrCountryCode("86");
        oplogInfo.setAddrCountryCode("中国");
        oplogInfo.setAddrPoveCode("");
        oplogInfo.setAddrPoveName("");
        oplogInfo.setAddrCityCode("");
        oplogInfo.setAddrCityName("");

        /* 追加日志基础信息：功能名称、操作名称、操作类型等 */
        oplogInfo.setOplogFunc(permissionInfo.getMenu());
        oplogInfo.setOplogType(permissionInfo.getType());
        oplogInfo.setOplogTitle(permissionInfo.getName());
        oplogRpc.saveOplog(oplogInfo);

        return oplogId;
    }

    /***
     * 追加日志基础信息
     *
     * @author 王大宸
     * @date 2025/2/10 10:16
     * @param userInfoDetails userInfoDetails
     * @param permissionInfo permissionInfo
     * @param oplogId oplogId
     * @return void
     */
    @Override
    public void appendFuncOplog(UserInfoDetails userInfoDetails, PermissionInfo permissionInfo, String oplogId) {
        if (null == oplogId) {
            return;
        }
        OplogInfoClient oplogInfoClient = getBizOplogInfo(userInfoDetails, oplogId);
        if (null == permissionInfo) {
            /* 如果不需要访问权限, 则删除日志 */
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    oplogRpc.remove(oplogInfoClient);
                }
            });
            return;
        }
        /* 追加日志基础信息：功能名称、操作名称、操作类型等 */
        oplogInfoClient.setOplogFunc(permissionInfo.getMenu());
        oplogInfoClient.setOplogType(permissionInfo.getType());
        oplogInfoClient.setOplogTitle(permissionInfo.getName());

        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                oplogRpc.appendOplog(oplogInfoClient);
            }
        });

    }


    /***
     * 追加日志请求体
     *
     * @author 王大宸
     * @date 2025/2/14 17:16
     * @param userInfoDetails userInfoDetails
     * @param body body
     * @param oplogId oplogId
     * @return void
     */
    @Override
    public void appendRequestBody(UserInfoDetails userInfoDetails, String body, String oplogId) {
        if (null == oplogId) {
            return;
        }
        OplogInfoClient oplogInfo = getBizOplogInfo(userInfoDetails, oplogId);
        oplogInfo.setRequestBody(body);
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                oplogRpc.appendOplog(oplogInfo);
            }
        });


    }

    /***
     * 追加日志响应信息
     *
     * @author 王大宸
     * @date 2025/2/10 11:02
     * @param userInfoDetails 用户信息
     * @param body            响应数据
     * @param oplogId         日志ID
     * @return void
     */
    @Override
    public void appendResponseBody(UserInfoDetails userInfoDetails, String body, String oplogId) {
        if (null == oplogId) {
            return;
        }
        JSONObject res = JSONObject.parseObject(body);

        OplogInfoClient oplogInfo = getBizOplogInfo(userInfoDetails, oplogId);
        oplogInfo.setResponseBody(body);
        oplogInfo.setResponseCode(res.getString("status"));
        oplogInfo.setResponseMsg(res.getString("msg"));
        oplogInfo.setResponseStatus(res.getString("success"));
        oplogInfo.setResponseTime(LocalDateTime.now());


        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                oplogRpc.appendResponseOplog(oplogInfo);
            }
        });
    }

    /***
     * 权限不足
     *
     * @author 王大宸
     * @date 2025/2/10 10:17
     * @param userInfoDetails userInfoDetails
     * @param permissionInfo permissionInfo
     * @param oplogId oplogId
     * @return void
     */
    @Override
    public void deniedPermission(UserInfoDetails userInfoDetails, PermissionInfo permissionInfo, ServerResponse<Object> resBody, String oplogId) {
        if (null == oplogId) {
            return;
        }

        String jsonString = JSONObject.toJSONString(resBody.getData());
        OplogInfoClient oplogInfo = getBizOplogInfo(userInfoDetails, oplogId);
        oplogInfo.setResponseBody(ServerResponse.getErrBody(resBody.getStatus(), resBody.getMsg(), jsonString));
        oplogInfo.setResponseCode(String.valueOf(resBody.getStatus()));
        oplogInfo.setResponseMsg(resBody.getMsg());
        oplogInfo.setResponseStatus(String.valueOf(resBody.getSuccess()));
        oplogInfo.setResponseTime(LocalDateTime.now());

        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                oplogRpc.deniedPermission(oplogInfo);
            }
        });
    }

    /***
     * 封装操作日志基础信息
     *
     * @author 王大宸
     * @date 2025/2/10 10:51
     * @param userInfoDetails userInfoDetails
     * @param oplogId oplogId
     * @return com.github.itdachen.auth.interfaces.entity.OplogInfoClient
     */
    private OplogInfoClient getBizOplogInfo(UserInfoDetails userInfoDetails, String oplogId) {
        if (null == oplogId) {
            return null;
        }
        return OplogInfoClient.builder()
                .id(oplogId)
                .platId(userInfoDetails.getPlatId())
                .platTitle(userInfoDetails.getPlatName())
                .appId(userInfoDetails.getAppId())
                .appName(userInfoDetails.getAppName())
                .appVersion(userInfoDetails.getAppVersion())

                /* 用户基础信息 */
                .tenantId(userInfoDetails.getTenantId())
                .tenantTitle(userInfoDetails.getTenantTitle())
                .userId(userInfoDetails.getId())
                .nickName(userInfoDetails.getNickName())
                .roleId(userInfoDetails.getRoleId())
                .roleName(userInfoDetails.getRoleName())

                /* 用户部门信息 */
                .deptId(userInfoDetails.getDeptId())
                .deptLevel(userInfoDetails.getDeptLevel())
                .deptTitle(userInfoDetails.getDeptTitle())
                .deptParentId(userInfoDetails.getDeptParentId())

                .build();
    }


    /***
     * 获取浏览器信息
     *
     * @author 王大宸
     * @date 2025/2/10 11:18
     * @param userAgent userAgent
     * @return java.lang.String
     */
    private String getBrowserInfo(String userAgent) {
        if (null == userAgent || userAgent.isEmpty()) {
            return "未知浏览器";
        }
        String userLowerCase = userAgent.toLowerCase();

        String browser = null;
        if (userLowerCase.contains("edge")) {
            browser = (userAgent.substring(userAgent.indexOf("Edge")).split(" ")[0]).replace("/", "-");
        } else if (userLowerCase.contains("msie")) {
            String substring = userAgent.substring(userAgent.indexOf("MSIE")).split(";")[0];
            browser = substring.split(" ")[0].replace("MSIE", "IE") + "-" + substring.split(" ")[1];
        } else if (userLowerCase.contains("safari") && userLowerCase.contains("version")) {
            browser = (userAgent.substring(userAgent.indexOf("Safari")).split(" ")[0]).split("/")[0]
                    + "-" + (userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1];
        } else if (userLowerCase.contains("opr") || userLowerCase.contains("opera")) {
            if (userLowerCase.contains("opera")) {
                browser = (userAgent.substring(userAgent.indexOf("Opera")).split(" ")[0]).split("/")[0]
                        + "-" + (userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1];
            } else if (userLowerCase.contains("opr")) {
                browser = ((userAgent.substring(userAgent.indexOf("OPR")).split(" ")[0]).replace("/", "-"))
                        .replace("OPR", "Opera");
            }
        } else if (userLowerCase.contains("chrome")) {
            browser = (userAgent.substring(userAgent.indexOf("Chrome")).split(" ")[0]).replace("/", "-");
        } else if ((userLowerCase.indexOf("mozilla/7.0") > -1) || (userLowerCase.indexOf("netscape6") != -1) ||
                (userLowerCase.indexOf("mozilla/4.7") != -1) || (userLowerCase.indexOf("mozilla/4.78") != -1) ||
                (userLowerCase.indexOf("mozilla/4.08") != -1) || (userLowerCase.indexOf("mozilla/3") != -1)) {
            browser = "Netscape-?";
        } else if (userLowerCase.contains("firefox")) {
            browser = (userAgent.substring(userAgent.indexOf("Firefox")).split(" ")[0]).replace("/", "-");
        } else if (userLowerCase.contains("rv")) {
            String IEVersion = (userAgent.substring(userAgent.indexOf("rv")).split(" ")[0]).replace("rv:", "-");
            browser = "IE" + IEVersion.substring(0, IEVersion.length() - 1);
        } else {
            // browser = "UnKnown, More-Info: " + userAgent;
            browser = "UnKnown";
        }
        return browser;
    }

    /***
     * 获取操作系统信息
     *
     * @author 王大宸
     * @date 2025/2/10 11:19
     * @param userAgent userAgent
     * @return java.lang.String
     */
    private String getOsInfo(String userAgent) {
        if (null == userAgent || userAgent.isEmpty()) {
            return "未知操作系统";
        }
        String os = "";
        if (userAgent.toLowerCase().contains("windows")) {
            os = "Windows";
        } else if (userAgent.toLowerCase().contains("mac")) {
            os = "Mac";
        } else if (userAgent.toLowerCase().contains("x11")) {
            os = "Unix";
        } else if (userAgent.toLowerCase().contains("android")) {
            os = "Android";
        } else if (userAgent.toLowerCase().contains("iphone")) {
            os = "IPhone";
        } else {
            //   os = "UnKnown, More-Info: " + userAgent;
            os = "UnKnown";
        }
        return os;
    }

}
